package com.instpower.things.instpower;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.instpower.things.transport.*;
import com.instpower.things.helper.Checksum;
import com.instpower.things.service.InstService;
import io.netty.buffer.ByteBuf;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;

/**
 * @ClassName Handler
 * @Author: hellmash@gmail.com
 * @Date: 2020/3/1
 * @Version: 1.0.0
 * @Description: 抽象类
 */
public abstract class Handler<T> {
    protected final static Logger logger = LoggerFactory.getLogger("CORE");

    protected InstService instService;

    /**
     * json
     */
    protected final static ObjectMapper mapper = new ObjectMapper();

    /**
     * 处理ID
     * @return
     */
    public abstract short getId();

    public void setServerHandler(InstService h){
        instService = h;
    }

    /**
     *
     * @param connection
     * @param data
     */
    public abstract void execute(Connection connection, T data) throws UnsupportedEncodingException;


    /**
     * 通用的返回方法
     * @param connection
     * @param response
     * @return	返回byte数组内容
     */
    public void sendResponse(Connection connection, Response response){

        DataBuffer buffer = response.encode();
        int checksum = Checksum.crc16(Checksum.CRC16_ANKER,
                ByteBuffer.wrap(buffer.readByteArray(buffer.readableBytes())));
        //release after read
        buffer.release();

        Ender ender = new Ender();
        ender.setChecksum(checksum);
        response.setEnder(ender);

        connection.sendResponse(response);
    }

    /**
     * 通用的报文处理,但不做任何响应,主要是应用于app向手表发送的指令回馈
     * @param connection
     * @param request
     * @return
     */
    public HandlerResult doHandlerWithoutResponse(Connection connection, Request request){
        logger.info("doHandlerWithoutResponse");
        Header reqHeader = request.getHeader();

        MTData reqData = request.getData();
        Ender reqEnder = request.getEnder();

        logger.info("doHandlerWithoutResponse 1");

        boolean isASuccess = this.validate(reqHeader, reqData, reqEnder);

        logger.info("doHandlerWithoutResponse 2");
        if(isASuccess){
            logger.info("doHandlerWithoutResponse 4");
            return new HandlerResult(reqData);
        }
        logger.info("doHandlerWithoutResponse 3");
        return null;
    }

    /**
     * 校验报文校验和
     * @param header
     * @param datas
     * @param ender
     * @return
     */
    public boolean validate(Header header, MTData datas, Ender ender){
        int resultCrc = GetCheckCode(header, datas.getDatas());

        logger.info("resultCRC = " + resultCrc + ", enderCRC = " + ender.getChecksum());
        if(resultCrc == ender.getChecksum()){
            return Boolean.TRUE;
        }

        return Boolean.FALSE;
    }


    public int GetCheckCode(Header header, byte[] datas){
        int checkLength = header.getLength();

        DataBuffer buffer = new DataBuffer(checkLength);
        buffer.writeShort(header.getCommand());

        if(datas != null && datas.length > 0) {
            buffer.writeBytes(datas);
        }
        byte[] bytes= buffer.readBytes(buffer.readableBytes());

        return Checksum.crc16(Checksum.CRC16_ANKER, ByteBuffer.wrap(bytes));
    }

    /**
     * 读取内容
     * @param byteBuf
     * @return
     */
    protected byte[] readBytes(ByteBuf byteBuf){
        byte[] reqData;
        //pooled,堆缓冲区
        if(byteBuf.hasArray()){
            reqData = byteBuf.array();
        }else{
            //Unpooled,4.1后默认使用直接缓冲区
            int len = byteBuf.readableBytes();
            reqData = new byte[len];
            byteBuf.getBytes(0, reqData);
            //byteBuf.release();
        }
        return reqData;
    }

    /**
     * logger信息
     */
    private final static String LogFormat = "Addr(SN)='%s',%s";
    protected String getLoggerInfo(Request request, String content){
        return String.format(LogFormat, request.getHeader().getAddr(), content);
    }
}
